#ifndef VL_FIXED_HPP
#define VL_FIXED_HPP

#include <cassert>
#include "angle.hpp"
#include "vec.hpp"

namespace vl
{

int const fixed_pi = 205887;
int const fixed_2pi = 411774;

struct fixed
{
	struct literal_tag
	{
	};
	
	fixed()
	: v(0)
	{
	}
	
	fixed(int n)
	: v(n << 16)
	{
		assert(n >= -(1 << 15) && n < (1 << 15));
	}
	
	fixed(int v, literal_tag)
	: v(v)
	{
		
	}
	
	fixed(double v)
	: v(int(v * double(1 << 16)))
	{
	}
	
	fixed operator-() const
	{ return fixed(-v, literal_tag()); }
	
	fixed& operator+=(fixed b)
	{ v += b.v;	return *this; }
	
	fixed operator+(fixed b) const
	{ return fixed(*this) += b; }
	
	fixed& operator-=(fixed b)
	{ v -= b.v;	return *this; }
	
	fixed operator-(fixed b) const
	{ return fixed(*this) -= b; }
	
	fixed& operator*=(fixed b)
	{
		v = static_cast<int>((static_cast<long long>(v) * b.v) >> 16);
		return *this;
	}
	
	fixed& operator/=(fixed b)
	{
		v = static_cast<int>((static_cast<long long>(v) << 16) / b.v);
		return *this;
	}
	
	fixed operator*(fixed b) const
	{ return fixed(*this) *= b; }
	
	fixed operator/(fixed b) const
	{ return fixed(*this) /= b; }
	
	fixed half() const
	{
		return from_literal(v / 2);
	}
	
	int to_int() const
	{
		return (v >> 16);
	}
	
	double to_double() const
	{
		return v / 65536.0;
	}
	
	int to_rep() const
	{
		return v;
	}
	
	angle to_angle() const
	{
		return angle((*this * vl::fixed(fixed_2pi, literal_tag())).to_rep());
	}
	
	static fixed from_literal(int v)
	{
		return fixed(v, literal_tag());
	}
	
	static int cos_table[4096];
	static int sin_table[4096];
	static int angle_table[4097];
	
	static fixed cos(angle a)
	{
		return from_literal(cos_table[a.adjust<12>() & 4095]);
	}
	
	static fixed sin(angle a)
	{
		return from_literal(sin_table[a.adjust<12>() & 4095]);
	}
	
	static basic_vec<fixed> vec_of(angle a)
	{
		return basic_vec<fixed>(cos(a), sin(a));
	}
	
	static angle angle_of(fixed x, fixed y);
	
	static void generate_tables();
	static void print_tables();
	
	bool operator<(fixed b) const { return v < b.v; }
	bool operator>(fixed b) const { return v > b.v; }
	bool operator<=(fixed b) const { return v <= b.v; }
	bool operator>=(fixed b) const { return v >= b.v; }
	bool operator==(fixed b) const { return v == b.v; }
	bool operator!=(fixed b) const { return v != b.v; }
	
	operator bool() const
	{
		return v != 0;
	}
	
	int v;
};

angle angle_of(basic_vec<fixed> v);

}

#endif // VL_FIXED_HPP
